/*
 *  eth.h
 *  xcode
 *
 *  Created by Emiel Goor,van on 25-01-08.
 *  Copyright 2008 __MyCompanyName__. All rights reserved.
 *
 */

// ethernet stuff

#include "eth.h"
#include "pack.h"
#include "adapt.h"
#include "ip.h"
#include "arp.h"
#include "pci.h"

struct ethernet_t *ethDevs[250];
int ethCount = 0;

netAdapter_t *ethGetAdapter(int index)
{
	if(index < 0 || index >= ethCount)
		return (netAdapter_t*) NULL;
	return ethDevs[index]->adapter;
}

netAdapter_t *ethGetAdapterByName(char *device)
{
	int index = 0;
	char devnum;
	if(!strcmp(device, "en", 2))
	{
		devnum = device[2];
		if(devnum < 0x30 || devnum > 0x39)
			return (netAdapter_t*) NULL;

		index = devnum - 0x30;
	}

	if(index >= ethCount)
		return (netAdapter_t*) NULL;
	return ethGetAdapter(index);
}

/* Returns pointer to packet->data + sizeof(ethHeader_t) */
void *ethernetStripHeader(packet_t *p)
{
	return (void *)(((char *)p->data) + sizeof(ethHeader_t));
}

int sendPacketEthernet(struct ethernet_t *eth, packet_t *p)
{
	struct ethHeader_t *ethPkt;
	packet_t *pkt;
	netAdapter_t* nic;
	nic = eth->adapter;
	
	// we need the IP header again
	ipHeader_t *ip = (ipHeader_t *) ( p->data );
	

	
	// get the destination IP
	unsigned int destIp = ip->ipDest;
	
	pkt = packetAddHeader(sizeof(struct ethHeader_t), p);
	if(!pkt || !pkt->data)
	{
		return 0;
	}

	/** ARP handling for MAC **/
	
	// get the network
	unsigned int network = destIp & nic->netmask;
	
	// ethernet packet
	ethPkt = (struct ethHeader_t *)pkt->data;
	
	// arp entry for eth addrs
	arpent theEnt;

/*
// check if the ip is on the same network
	if( network == nic->network )
	{

		// same network - find via ARP
		if( checkArpCache( destIp, &theEnt ) )
		{
			// we have the location
			ethPkt->dest[0] = theEnt.ethAddr[0];
			ethPkt->dest[1] = theEnt.ethAddr[1];
			ethPkt->dest[2] = theEnt.ethAddr[2];
			ethPkt->dest[3] = theEnt.ethAddr[3];
			ethPkt->dest[4] = theEnt.ethAddr[4];
			ethPkt->dest[5] = theEnt.ethAddr[5];
		}
		else
		{
			// try to find the client
			if( findClient( destIp, &theEnt ) != -1 )
			{
				ethPkt->dest[0] = theEnt.ethAddr[0];
				ethPkt->dest[1] = theEnt.ethAddr[1];
				ethPkt->dest[2] = theEnt.ethAddr[2];
				ethPkt->dest[3] = theEnt.ethAddr[3];
				ethPkt->dest[4] = theEnt.ethAddr[4];
				ethPkt->dest[5] = theEnt.ethAddr[5];
			}
			else
			{
				// drop it, client isn't active
				nic->dropped++;
				
				// fail
				return 0;
			}
		}
	}
	else
	{
	
		// different network, find and send to the gateway
		if( checkArpCache( nic->gateway, &theEnt ) )
		{
			// gateway mac is in cache
			ethPkt->dest[0] = theEnt.ethAddr[0];
			ethPkt->dest[1] = theEnt.ethAddr[1];
			ethPkt->dest[2] = theEnt.ethAddr[2];
			ethPkt->dest[3] = theEnt.ethAddr[3];
			ethPkt->dest[4] = theEnt.ethAddr[4];
			ethPkt->dest[5] = theEnt.ethAddr[5];
		}
		else
		{
			// find the gateway
			if( findClient( nic->gateway, &theEnt ) != 1 )
			{
				ethPkt->dest[0] = theEnt.ethAddr[0];
				ethPkt->dest[1] = theEnt.ethAddr[1];
				ethPkt->dest[2] = theEnt.ethAddr[2];
				ethPkt->dest[3] = theEnt.ethAddr[3];
				ethPkt->dest[4] = theEnt.ethAddr[4];
				ethPkt->dest[5] = theEnt.ethAddr[5];
			}
			else
			{
				// drop the packet - gateway is invalid
				nic->dropped++;
				
				// fail
				return 0;
			}
		}
	}
	
	*/


	memcpy(ethPkt->src, eth->macAddr, 6);
	ethPkt->type = htons(ETHER_FRAME_IP);

	// send the packet
	eth->send(eth, pkt);
	
	return 1;	
}

void handleEthernet(struct ethernet_t *eth, packet_t *p)
{
	struct ethHeader_t *ethPkt;
	ethPkt = (struct ethHeader_t *)p->data;

	switch(ethPkt->type)
	{
		case htons(ETHER_FRAME_IP):
			printf( "IP Packet\n" );
			handleIpPacket(eth, p);
			break;
		case htons(ETHER_FRAME_ARP):
			printf( "ARP Packet\n" );
			handleArp(eth, p);
			break;
		default:
			printf( "Unknown ethernet frame\n" );
			break;
	}
}

int addEthernetCard(struct ethernet_t *card)
{
	struct ethernet_t **new;
	ethDevs[ethCount++] = card;
	netAddAdapter(NET_TYPE_ETHER, card);
	card->adapter->overhead = ETHER_HLEN;
	printf("Ethernet: New network adapter, index %d\n", ethCount-1);
	return 1;
}